package com.gome.ocean.service.base;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang3.StringUtils;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.message.BasicNameValuePair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.github.pagehelper.Page;
import com.gome.framework.AppContext;
import com.gome.framework.Env;
import com.gome.ocean.common.constants.Constants;
import com.gome.ocean.common.exception.AuthorityManagerException;
import com.gome.ocean.common.exception.BaseControllerException;
import com.gome.ocean.common.exception.ProjectServiceException;
import com.gome.ocean.common.utils.HttpClientUtil;
import com.gome.ocean.common.utils.ResponsePage;
import com.gome.ocean.dao.model.authority.BaseOnProjectEntity;
import com.gome.ocean.dao.model.authority.ProjectBO;
import com.gome.ocean.dao.model.authority.User;
import com.gome.ocean.dao.model.cango.CangoInstances;
import com.gome.ocean.dao.model.cango.CangoResponseVO;
import com.gome.ocean.service.authority.AuthorityManager;
import com.gome.ocean.service.authority.ProjectService;
import com.gome.ocean.service.webservice.staff.Service0004Soap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

/**
 * Created by liuqingxia on 2016/6/24.
 */
public class AuthorityBaseController extends BaseController {

    private static final Logger LOG             = LoggerFactory.getLogger(AuthorityBaseController.class);

    protected final String      AUTHORITY_ERROR = "您没有执行操作的权限";

    @Autowired
    protected ProjectService    projectService;
    @Autowired
    private AuthorityManager    authorityManager;
    @Autowired
    private Service0004Soap     iAccountService;

    /**
     * 判断是否是admin用户
     */
    protected boolean isAdmin(HttpServletRequest req) {
        User user = (User) req.getSession().getAttribute(Constants.SESSION_LOGIN_USER);
        String role = user.getRole();
        return role.equals("admin");
    }

    /**
     * 判断是否是DBA用户
     */
    protected boolean isDBA(HttpServletRequest req) {
        User user = (User) req.getSession().getAttribute(Constants.SESSION_LOGIN_USER);
        String role = user.getRole();
        return role.equals("dba");
    }

    /**
     * 获取不同环境的DBA接口ip
     */
    protected String getDBAInterfaceIp() {
        // Env env = AppContext.getEnv();
        // String ipStr = env.get("dba_interface_ip");
        String ipStr = "websql.ds.gome.com.cn";
        if (StringUtils.isNotEmpty(ipStr)) {
            return ipStr;
        }
        return "";
    }

    protected String getDiffEnvOceanIp() {
        Env env = AppContext.getEnv();
        String diffStr = env.get("environValue").toUpperCase();
        if (StringUtils.isNotEmpty(diffStr)) {
            String abc = "";
            switch (diffStr) {
                case "SIT":
                    abc = "127.0.0.1";
                    break;
                case "UAT":
                    abc = "10.58.57.102";
                    break;
                case "PRE":
                    abc = "10.58.57.104";
                    break;
                case "LIVE":
                    abc = "10.58.69.54";
                    break;
            }
            return abc;
        }
        return "";
    }

    /**
     * 获取不同环境的 环境类型参数值
     */
    protected String getDiffEnvironmentValue() {
        Env env = AppContext.getEnv();
        String diffStr = env.get("environValue").toUpperCase();
        if (StringUtils.isNotEmpty(diffStr)) {
            return diffStr.equals("LIVE") ? "PRD" : diffStr;
        }
        return "";
    }

    /**
     * 获取不同环境 短信登录验证的值
     */
    protected String getDiffEnvSmsValue() {
        Env env = AppContext.getEnv();
        String smsLoginDiamondValue = env.get("smsLoginValidateFlag");
        if (StringUtils.isNotBlank(smsLoginDiamondValue)) {
            return smsLoginDiamondValue;
        }
        return "";
    }

    /**
     * 根据http路径 访问接口
     */
    protected Map<String, Object> executeHttpClientPostUrl(String path) {
        Map<String, Object> map = Maps.newHashMap();
        HttpClientUtil httpClientUtil = new HttpClientUtil();
        try {
            URL url = new URL(path);
            HttpPost httpPost = HttpClientUtil.getPostRequest();
            URI uri = new URI(url.getProtocol(), url.getHost(), url.getPath(), url.getQuery(), null);
            httpPost.setURI(uri);
            String jsonCallback = httpClientUtil.executeAndGetWithFailedRetry(httpPost, 6, 10001);
            JSONArray array = JSONArray.parseArray(jsonCallback);
            if (array.getString(0).toLowerCase().equals("ok")) {
                map.put("state", "success");
                map.put("info", array.getString(0));
                return map;
            } else if (array.getString(0).toLowerCase().equals("error")) {
                map.put("state", "error");
                map.put("info", array.getString(1));
                return map;
            }
        } catch (URISyntaxException | MalformedURLException e) {
            LOG.error(e.getMessage(), e);
            map.put("state", "error");
            map.put("info", e.getMessage());
        }
        return map;
    }

    protected String getDepartMentByAccount(String account) throws RemoteException {
        StringBuilder sb = new StringBuilder();
        sb.append("\"").append(account).append("\"");
        String userInfoJson = iAccountService.getStaffList(sb.toString());
        JSONObject jsonObj = JSON.parseObject(userInfoJson);
        String status = jsonObj.getString("errormsg");
        if (status.equals("success")) {
            JSONArray jsonArray = jsonObj.getJSONArray("list");
            JSONObject userInfo = jsonArray.getJSONObject(0);
            String departName = userInfo.getString("deptName");
            if (StringUtils.isNoneBlank(departName)) {
                return departName;
            }
        }
        return "";
    }

    protected String getDepartCodeByAccount(String account) throws RemoteException {
        StringBuilder sb = new StringBuilder();
        sb.append("\"").append(account).append("\"");
        String userInfoJson = iAccountService.getStaffList(sb.toString());
        JSONObject jsonObj = JSON.parseObject(userInfoJson);
        String status = jsonObj.getString("errormsg");
        if (status.equals("success")) {
            JSONArray jsonArray = jsonObj.getJSONArray("list");
            JSONObject userInfo = jsonArray.getJSONObject(0);
            String departCode = userInfo.getString("deptCode");
            if (StringUtils.isNoneBlank(departCode)) {
                return departCode;
            }
        }
        return "";
    }

    protected boolean isAdmin() {
        return isAdmin(getHttpRequest());
    }

    protected boolean isDBA() {
        return isDBA(getHttpRequest());
    }

    /**
     * 获取当前用户所属项目组
     * 
     * @param req
     * @return
     */
    protected List<String> getCurrentUserProjects(HttpServletRequest req) {
        String email = getCurrentUser(req);
        List<String> projectNames = Lists.newArrayList();
        List<ProjectBO> projects = null;
        try {
            projects = projectService.findAllProjectByCurrentEmail(email);
        } catch (Exception e) {
            LOG.error(e.getMessage(), e);
        }
        if (null == projects || projects.isEmpty()) {
            return null;
        }
        for (ProjectBO project : projects) {
            projectNames.add(project.getProjectName());
        }
        return projectNames;
    }

    /**
     * 获取当前用户所属项目组
     */
    protected List<String> getCurrentUserProjects() {
        return getCurrentUserProjects(getHttpRequest());
    }

    /**
     * 是否允许访问当前BaseOnProjectEntity对象
     *
     * @param entity
     * @return
     */
    protected boolean permitAccess(BaseOnProjectEntity entity) throws AuthorityManagerException {
        return authorityManager.permitAccess(getCurrentUserObj(), entity).isPermit();
    }

    /**
     * 获取项目组列表 如果是管理员，<br>
     * </> 则获取所有项目组列表， <br>
     * </> 如果不是管理员，则获取当前用户所在的项目组
     */
    protected List<String> getProperProjects() throws BaseControllerException {
        List<String> projects;
        if (isAdmin()) {
            projects = getAllProjectNames();
        } else {
            projects = getCurrentUserProjects();
        }
        return projects;
    }

    /**
     * 获取所有的项目组
     * 
     * @return
     * @throws BaseControllerException
     */
    protected List<String> getAllProjectNames() throws BaseControllerException {
        List<String> projectNames = Lists.newArrayList();
        try {
            List<ProjectBO> projects = projectService.findAllProjects(DEFAULT_MIN_PROJECT_ROWS,
                DEFAULT_MAX_PROJECT_ROWS);
            for (ProjectBO project : projects) {
                projectNames.add(project.getProjectName());
            }
        } catch (ProjectServiceException e) {
            LOG.error(e.getMessage(), e);
            throw new BaseControllerException(e.getMessage());
        }
        return projectNames;
    }

    /**
     * 批量查询BaseOnProjectEntity
     * 
     * @param service
     * @param entityName
     * @param isPage
     * @return
     */
    protected ResponsePage findEntityBachWithAuth(Object service, String entityName, boolean isPage) {

        ResponsePage response = getResponsePage();
        List<Object> result;
        Page page;

        String methodName = genMethodNameByAdmin(entityName);
        Class[] classes = getArgumentTypesAdmin(isPage);

        try {
            Method findMethod = getMethod(service, methodName, classes);
            Object[] arguments;
            if (isAdmin()) {
                arguments = getArguments(isPage);
            } else {
                List<String> projectNames = getCurrentUserProjects();
                if (null == projectNames || projectNames.isEmpty()) {
                    return response.setErrorMsg(NULL_PROJECT);
                }
                arguments = getArgumentsWithProject(isPage, projectNames);
            }
            result = invoke(service, findMethod, arguments);
            if (isPage) {
                page = (Page) result;
                return response.setPage(page, FIND_SUCCESS);
            }
            return response.setJustList(result, FIND_SUCCESS);
        } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
            LOG.error(e.getMessage(), e);
            return response.setErrorMsg(EXCEPTION_MSG);
        }

    }

    private Class[] getArgumentTypesAdmin(boolean isPage) {
        return isAdmin() ? getArgumentTypes(isPage) : getArgumentTypesWithProject(isPage);
    }

    private String genMethodNameByAdmin(String entityName) {
        return isAdmin() ? genFindMethName(entityName) : genFindMethNameWithProject(entityName);
    }

    private String genFindMethName(String entityName) {
        return "findAll".concat(entityName + "s");
    }

    private String genFindMethNameWithProject(String entityName) {
        return genFindMethName(entityName).concat("ByProjectNames");
    }

    private Method getMethod(Object service, String methodName, Class[] classes) throws NoSuchMethodException {
        Class<?> serviceClass = service.getClass();
        Method method = serviceClass.getDeclaredMethod(methodName, classes);
        return method;
    }

    private Class[] getArgumentTypes(boolean isPage) {
        Class[] classes = isPage ? new Class[] { Integer.class, Integer.class } : new Class[] {};
        return classes;
    }

    private Class[] getArgumentTypesWithProject(boolean isPage) {
        Class[] classes = isPage ? new Class[] { List.class, Integer.class,
                                                 Integer.class } : new Class[] { List.class };
        return classes;
    }

    private List<Object> invoke(Object service, Method method, Object[] arguments) throws InvocationTargetException,
                                                                                   IllegalAccessException {
        method.setAccessible(true);
        List<Object> result = (List<Object>) method.invoke(service, arguments);
        return result;
    }

    private Object[] getArguments(boolean isPage) {
        return isPage ? new Object[] { getPageNum(), getPageSize() } : new Object[] {};
    }

    private Object[] getArgumentsWithProject(boolean isPage, List<String> projectNames) {
        return isPage ? new Object[] { projectNames, getPageNum(), getPageSize() } : new Object[] { projectNames };
    }

    protected HttpPost buildHttpPost(String uri, CangoInstances cangoInstance) throws UnsupportedEncodingException,
                                                                               IllegalAccessException {
        HttpPost httpPost = new HttpPost(uri);
        UrlEncodedFormEntity formEntity = buildFormEntity(cangoInstance);
        httpPost.setEntity(formEntity);
        return httpPost;
    }

    private UrlEncodedFormEntity buildFormEntity(CangoInstances cango) throws IllegalAccessException,
                                                                       UnsupportedEncodingException {
        List<NameValuePair> nameValuePairs = new ArrayList<>();
        Class<CangoInstances> dtoClass = CangoInstances.class;
        Field[] declaredFields = dtoClass.getDeclaredFields();
        if (declaredFields != null && declaredFields.length > 0) {
            for (Field declaredField : declaredFields) {
                if (!Modifier.isStatic(declaredField.getModifiers())) {
                    String declaredFieldName = declaredField.getName();
                    declaredField.setAccessible(true);
                    Object value = declaredField.get(cango);
                    if (value != null) {
                        Class<?> type = declaredField.getType();
                        if (type.isPrimitive() || Integer.class.equals(type) || String.class.equals(type)
                            || type.isEnum()) {
                            nameValuePairs.add(new BasicNameValuePair(declaredFieldName, value.toString()));
                        }

                        if (Set.class.isAssignableFrom(type)) {
                            Set<String> set = (Set) value;
                            StringBuilder stringBuilder = new StringBuilder();
                            for (String item : set) {
                                stringBuilder.append(item).append(",");
                            }

                            stringBuilder.deleteCharAt(stringBuilder.length() - 1);
                            nameValuePairs.add(new BasicNameValuePair(declaredFieldName, stringBuilder.toString()));
                        }
                    }

                    declaredField.setAccessible(false);
                }
            }
        }

        return new UrlEncodedFormEntity(nameValuePairs, "UTF-8");
    }

    protected HttpGet getHttpGet(String path) throws Exception {
        HttpClientUtil clientUtil = new HttpClientUtil();
        HttpGet httpGet = clientUtil.getGetRequest();
        URL url = new URL(path);
        httpGet.setURI(url.toURI());
        return httpGet;
    }

    protected CangoResponseVO parseResponse(HttpGet httpGet) throws IOException {
        HttpClientUtil clientUtil = new HttpClientUtil();
        String json = clientUtil.executeAndGetWithFailedRetry(httpGet, 1, 10001);
        CangoResponseVO cangoResponse = JSON.parseObject(json, CangoResponseVO.class);
        return cangoResponse;
    }
}
